package com.devshephard.rpcframework.rpc.service.rpcservice.impl;

import com.devshephard.rpcframework.rpc.factory.channel.SingletonClientChannelFactory;
import com.devshephard.rpcframework.rpc.handler.RpcResponseMessageHandler;
import com.devshephard.rpcframework.rpc.message.RpcRequestMessage;
import com.devshephard.rpcframework.rpc.protocol.SequenceIdGenerator;
import com.devshephard.rpcframework.rpc.util.HttpClientUtil;
import io.netty.util.concurrent.DefaultPromise;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert;

import java.lang.reflect.Proxy;

@Slf4j
public class RpcServiceTcpImpl<T> implements FactoryBean<T>, InitializingBean, ApplicationContextAware {

    /**
     * object类型
     */
    private Class<?> type;

    private ApplicationContext applicationContext;

    /**
     * 服务提供者名称
     */
    private String name;

    @Override
    public T getObject() {

        ClassLoader loader = type.getClassLoader();
        Class<?>[] interfaces = new Class[]{type};
        //进行代理
        Object obj = Proxy.newProxyInstance(loader, interfaces, (proxy, method, args) -> {
            // 1. 将方法调用转换为 消息对象
            int sequenceId = SequenceIdGenerator.nextId();
            //TODO 由于idea debug模式下为了显示信息 会自动调用代理类toString方法 这里先做处理不然调试不了
            if(method.getName().equals("toString")){
                return null;
            }
            RpcRequestMessage msg = new RpcRequestMessage(
                    sequenceId,
                    type.getName(),
                    method.getName(),
                    method.getReturnType(),
                    method.getParameterTypes(),
                    args
            );

            // 2. 准备一个空 Promise 对象，来接收结果             指定 promise 对象异步接收结果线程,即当promise创建监听器时,返回结果由getChannel().eventLoop()中的新线程执行（即62-66行代码）
            DefaultPromise<Object> promise = new DefaultPromise<>(SingletonClientChannelFactory.getChannel().eventLoop());
            RpcResponseMessageHandler.PROMISES.put(sequenceId, promise);

            // 3. 将消息对象发送出去
            SingletonClientChannelFactory.getChannel().writeAndFlush(msg);

            /*  promise.addListener(future -> {
                // 线程
                // 这里的 future 就是上面的 promise
                System.out.println(Thread.currentThread().getName() + " " + future.getNow());
            });*/

            // 4. 等待 promise 结果

            promise.await();
            if(promise.isSuccess()) {
                // 调用正常
                return promise.getNow();
            } else {
                // 调用失败
                throw new RuntimeException(promise.cause());
            }
        });

        return (T) obj;

    }

    @Override
    public boolean isSingleton() {
        return false;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.hasText(name, "Name must be set");
    }

    @Override
    public Class<?> getObjectType() {
        return type;
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        applicationContext = context;
    }

    public Class<?> getType() {
        return type;
    }

    public void setType(Class<?> type) {
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
